package proxy

import (
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"time"

	"apiGateWay/internal/config"
	"apiGateWay/internal/database"
	"apiGateWay/internal/model"
	"apiGateWay/pkg/logger"

	"github.com/gin-gonic/gin"
)

// ProxyHandler 代理处理器
type ProxyHandler struct {
	route config.RouteConfig
	cfg   *config.Config
	client *http.Client
}

// NewProxyHandler 创建代理处理器
func NewProxyHandler(route config.RouteConfig, cfg *config.Config) *ProxyHandler {
	return &ProxyHandler{
		route: route,
		cfg:   cfg,
		client: &http.Client{
			Timeout: 30 * time.Second,
		},
	}
}

// Handle 处理代理请求
func (p *ProxyHandler) Handle(c *gin.Context) {
	// 构建目标 URL
	targetURL := p.buildTargetURL(c)

	// 创建请求
	req, err := http.NewRequest(c.Request.Method, targetURL, c.Request.Body)
	if err != nil {
		logger.Errorf("创建请求失败: %v", err)
		c.JSON(http.StatusInternalServerError, gin.H{
			"error": "创建请求失败",
		})
		return
	}

	// 复制请求头
	p.copyHeaders(c, req)

	// 发送请求
	resp, err := p.client.Do(req)
	if err != nil {
		logger.Errorf("转发请求失败: %v", err)
		c.JSON(http.StatusBadGateway, gin.H{
			"error": "转发请求失败",
		})
		return
	}
	defer resp.Body.Close()

	// 复制响应头
	p.copyResponseHeaders(c, resp)

	// 复制响应体
	c.DataFromReader(resp.StatusCode, resp.ContentLength, resp.Header.Get("Content-Type"), resp.Body, nil)

	// 记录审计日志
	p.logAudit(c, resp)
}

// buildTargetURL 构建目标 URL
func (p *ProxyHandler) buildTargetURL(c *gin.Context) string {
	target := p.route.Target
	path := c.Param("path")

	// 处理路径匹配
	// 如果配置的路径包含通配符，需要提取实际请求的剩余路径
	if strings.Contains(p.route.Path, "*") {
		// 获取通配符后的路径部分
		requestPath := c.Request.URL.Path
		// 移除路由路径中通配符前的部分
		routePattern := p.route.Path
		if idx := strings.Index(routePattern, "*"); idx > 0 {
			prefix := routePattern[:idx]
			if strings.HasPrefix(requestPath, prefix) {
				// 提取通配符匹配的部分
				matchedPath := requestPath[len(prefix):]
				// 移除可能的通配符标记
				matchedPath = strings.TrimPrefix(matchedPath, "/")
				if matchedPath != "" {
					if !strings.HasSuffix(target, "/") {
						target += "/"
					}
					target += matchedPath
				}
			}
		}
	} else {
		// 没有通配符，直接拼接路径参数
		if path != "" {
			if !strings.HasSuffix(target, "/") {
				target += "/"
			}
			target += path
		}
	}

	// 添加查询参数
	if c.Request.URL.RawQuery != "" {
		target += "?" + c.Request.URL.RawQuery
	}

	return target
}

// copyHeaders 复制请求头
func (p *ProxyHandler) copyHeaders(c *gin.Context, req *http.Request) {
	// 复制所有请求头，但排除一些不应该转发的头
	for key, values := range c.Request.Header {
		lowerKey := strings.ToLower(key)
		// 跳过一些不应该转发的头
		if lowerKey == "host" || lowerKey == "connection" {
			continue
		}
		for _, value := range values {
			req.Header.Add(key, value)
		}
	}

	// 设置目标主机
	req.Host = strings.TrimPrefix(strings.TrimPrefix(p.route.Target, "http://"), "https://")
}

// copyResponseHeaders 复制响应头
func (p *ProxyHandler) copyResponseHeaders(c *gin.Context, resp *http.Response) {
	for key, values := range resp.Header {
		for _, value := range values {
			c.Writer.Header().Add(key, value)
		}
	}
}

// logAudit 记录审计日志
func (p *ProxyHandler) logAudit(c *gin.Context, resp *http.Response) {
	userID, _ := c.Get("user_id")
	username, _ := c.Get("username")

	// 记录到日志
	logger.Infow("API 请求审计",
		"route", p.route.Name,
		"method", c.Request.Method,
		"path", c.Request.URL.Path,
		"client_ip", c.ClientIP(),
		"user_id", userID,
		"username", username,
		"target", p.route.Target,
		"status_code", resp.StatusCode,
		"response_size", resp.ContentLength,
	)

	// 如果启用了数据库，保存到数据库
	if database.DB != nil {
		var userIDUint *uint
		if userIDStr, ok := userID.(string); ok && userIDStr != "" {
			// 尝试解析 user_id
			if id, err := strconv.ParseUint(userIDStr, 10, 32); err == nil {
				idUint := uint(id)
				userIDUint = &idUint
			}
		}

		auditLog := &model.AuditLog{
			UserID:       userIDUint,
			Username:     getStringValue(username),
			Route:        p.route.Name,
			Method:       c.Request.Method,
			Path:         c.Request.URL.Path,
			ClientIP:     c.ClientIP(),
			StatusCode:   resp.StatusCode,
			ResponseSize: resp.ContentLength,
			Level:        "info",
		}

		if resp.StatusCode >= 400 {
			auditLog.Level = "error"
		} else if resp.StatusCode >= 300 {
			auditLog.Level = "warn"
		}

		database.DB.Create(auditLog)
	}
}

func getStringValue(v interface{}) string {
	if v == nil {
		return ""
	}
	if s, ok := v.(string); ok {
		return s
	}
	return fmt.Sprintf("%v", v)
}

